#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#define NORMAL_COMMAND 1
#define INTERNAL_COMMAND 0
#define EMPTY_COMMAND -1
#define DOT_COMMAND 2


typedef void (*sighandler_t)(int);
char delims[] = {' ','\t'};
int arg_length=0;
int last_exit_value=0;

void handle_signal(int signo)
{
  printf("\nCSE451Shell%% ");
  fflush(stdout);
}

void free_argv(char *my_argv[])
{
  int index;
  for(index=0;my_argv[index]!=NULL;index++) {
    bzero(my_argv[index], strlen(my_argv[index])+1);
    my_argv[index] = NULL;
    free(my_argv[index]);
  }
}

void fill_argv(char *str, char *my_argv[])
{
  char *token = NULL;
  char *str_temp = (char *)malloc(sizeof(char)*(strlen(str)+1));
  strncpy(str_temp, str, strlen(str));
  str_temp[strlen(str)]='\0';
  token = strtok( str_temp, delims );
  int count=0;
  while( token != NULL ) {
    my_argv[count] = (char *)malloc(sizeof(char)*(strlen(token)+1));
    strncpy(my_argv[count], token, strlen(token));
    my_argv[count][strlen(token)]='\0';
    count++;
    token = strtok( NULL, delims );
  }
  my_argv[count] = NULL;
}

void print_args(char *my_argv[]){
  int i;
  for(i=0; my_argv[i]!=NULL;i++){
    printf("%s\n", my_argv[i]);
    int j;
    for(j=0;my_argv[i][j];j++){
      printf("%c %d\n", my_argv[i][j], my_argv[i][j]);
    }
  }
}

void execute_normal_command(char *user_input){
   pid_t  pid;
   int    status;
   pid =fork();
   if (pid == 0) {
     char *argv[100];
     fill_argv(user_input, argv);
     //print_args(argv);
     last_exit_value = execvp(*argv, argv);
     if (last_exit_value < 0){
       last_exit_value = 0;
       printf("command not found\n");
       free_argv(argv);
       exit(last_exit_value);
     }
   }else{
     /* for the parent:      */
     wait(NULL);
   }
}

/*
 * returns 
 * internal commands : 0
 * normal commands : 1
 * empty command : -1
 */
int is_internal_command(char *user_input){
  char *token = NULL;
  char *str_temp = (char *)malloc(sizeof(char)*strlen(user_input));
  int result;
  strncpy(str_temp, user_input, strlen(user_input));
  token = strtok( str_temp, delims );
  if (token!=NULL){
    if (strncmp(token,"exit",4)==0 || strncmp(token,"cd",2)==0 || strncmp(token,".",1)==0){
      if (strncmp(token,".",1)==0){
	result = DOT_COMMAND;
      }else{
	result = INTERNAL_COMMAND;
      }
    }else{
      result = NORMAL_COMMAND;
    } 
  }else{
    result = EMPTY_COMMAND;
  }
  free(str_temp);
  return result;
}

/*
 * if tmp is an non_dot_internal command
 */
void execute_internal_command(char *user_input){
  char *argv[100];
  fill_argv(user_input, argv);
  if (strncmp(*argv,"exit",4)==0){//exit
    if (argv[1]!=NULL){
      int exit_number = atoi(argv[1]);
      free_argv(argv);
      exit(exit_number);
    }else{//exit with exit's value of last command when n is not given
      exit(last_exit_value);
    }
  }else {//cd command
    if (argv[1]!=NULL){
      if (chdir(argv[1])!=0){// invalid path to directory
	printf("No such directory\n");
      }
    }else{//no argument is given, then default to HOME from environment variable
      chdir(getenv("HOME"));
    }
    free_argv(argv);
  }
}

/*
 * if tmp is an dot internal command
 */
void execute_dot_command(char *user_input){
  char *argv[100];
  fill_argv(user_input, argv);
  if (argv[1]==NULL){//check to see if filename is given
    free_argv(argv);
    printf("Usage: . filename\n");
  }else{//filename is give, ok to proceed
    char *filename = argv[1];
    if (read_a_file(filename)!=0){
      printf("read_a_file %s\n", filename);
      printf("No such file or directory\n");
    }
    free_argv(argv);
    //free(filename);
  }
}

void command_handler(char *user_input){
  int command_type = is_internal_command(user_input);
  if (command_type==INTERNAL_COMMAND){
    execute_internal_command(user_input);
  }else if (command_type==DOT_COMMAND){
    execute_dot_command(user_input);
  }else if (command_type==NORMAL_COMMAND){
    execute_normal_command(user_input);
  }
}

int read_a_file(char *file_name){
  FILE *fr;            /* declare the file pointer */
  char line[80];
  pid_t  pid;
  int    status;
  fr = fopen (file_name, "rt");  /* open the file for reading */
  /* elapsed.dta is the name of the file */
  /* "rt" means open the file for reading text */
  
  if (fr==NULL){
    return -1;
  }

  while(fgets(line, 80, fr)!=NULL) { 
    int len = strlen(line);
    if (len>0){
      if (line[len-1]=='\n'){
	line[len-1]='\0';
      }
    }
    command_handler(line);
    printf("\n");
  }
  fclose(fr);  /* close the file prior to exiting the routine */
  return 0;
}

int main(int argc, char *argv[], char *envp[])
{
  char c;
  char *tmp = (char *)malloc(sizeof(char) * 100);
  
  //register signal
  signal(SIGINT, SIG_IGN);//ignore the interupt
  signal(SIGINT, handle_signal);//handle signal
  
  printf("CSE451Shell%% ");
  while(c != EOF) {
    c = getchar();    
    switch(c) {
    case '\n': /* parse and execute. */
      command_handler(tmp);
      printf("CSE451Shell%% ");
      bzero(tmp, sizeof(tmp));//Zero Out Bytes in Memory
      break;
    default: strncat(tmp, &c, 1);
      break;
    }
  }
  printf("\n");
  free(tmp);
  return 0;
}

int arg_count(char *str, char delims[]){
  char *parts = NULL;
  parts = strtok( str, delims );
  int count=0;
  while( parts != NULL ) {
    count++;
    parts = strtok( NULL, delims );
  }
  return count;
}






